﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.IO;
using Microsoft.Xna.Framework.Content;
using System.Xml.Serialization;
using System.Windows.Forms;
using Debug;
using System.ComponentModel;

namespace Kristall
{
    [Serializable]
    public class AnimatableSprite
    {
        public float FrameTime
        {
            get;
            set;
        }


        float timer = 0;
        int frameIndex = 0;
        int frameCount = 1;

        [XmlIgnore]
        public Dictionary<string, Texture2D> SpriteTextures;

        [XmlIgnore]
        public Texture2D Texture
        {
            get
            {
                return SpriteTextures[FileName];
            }
        }

        [XmlIgnore]
        public int FrameCount
        {
            get
            {
                return frameCount;
            }
            set
            {
                frameCount = value;
                FrameWidth = SpriteTextures[FileName].Width / frameCount;
            }
        }

        int frameWidth = 0;
        public int FrameWidth
        {
            get
            {
                return frameWidth;
            }
            set
            {
                frameWidth = value;
                updateSource();
            }
        }

        int frameHeight = 0;
        public int FrameHeight
        {
            get
            {
                return frameHeight;
            }
            set
            {
                frameHeight = value;
                updateSource();
            }
        }

        public bool IsLooping
        {
            get;
            set;
        }

        public event EventHandler OnAnimationStart;
        public event EventHandler OnAnimationEnd;

        [XmlIgnore]
        public static string SpritesDir
        {
            get
            {
                return "Sprites";
            }
        }

        [XmlIgnore]
        public static string FullSpritesPath
        {
            get
            {
                return Application.StartupPath + Kristall.DS + "Content" + Kristall.DS + SpritesDir;
            }
        }


        public string FileName
        {
            get;
            set;
        }

        public float Rotation
        {
            get;
            set;
        }

        public bool Mirror
        {
            get;
            set;
        }

        public Color Tint
        {
            get;
            set;
        }

        public Vector2 Origin
        {
            get;
            set;
        }

        public Vector2 Offset { get; set; }

        public Vector2 Scale { get; set; }

        [XmlIgnore]
        private Rectangle source;

        public AnimatableSprite()
        {
            initialize("");
        }

        public AnimatableSprite(string fileName)
        {
            initialize(fileName);
        }

        //public AnimatableSprite(AnimatableSprite animatableSprite) : this()
        //{
        //    this.FileName = animatableSprite.FileName;
        //    this.LayerDepth = animatableSprite.LayerDepth;
        //    this.Tint = animatableSprite.Tint;
        //    this.Rotation = animatableSprite.Rotation;
        //    this.Mirror = animatableSprite.Mirror;
        //    this.Origin = animatableSprite.Origin;
        //    this.IsLooping = animatableSprite.IsLooping;

        //    this.FrameWidth = animatableSprite.FrameWidth;
        //    this.FrameHeight = animatableSprite.FrameHeight;
        //    this.SpriteTextures = animatableSprite.SpriteTextures;
        //}

        private void initialize(string fileName)
        {
            FileName = fileName;
            Tint = Color.White;
            Rotation = 0;
            Mirror = false;
            Origin = Vector2.Zero;
            IsLooping = true;
            FrameTime = 0.1f;
            Scale = Vector2.One;
            Offset = Vector2.Zero;
        }

        public virtual void LoadContent(Dictionary<string, Texture2D> SpriteTextures, ContentManager content)
        {
            this.SpriteTextures = SpriteTextures;

            if (SpriteTextures.ContainsKey(FileName))
            {
                if (!SpriteTextures[FileName].IsDisposed)
                {
                    //Nothing to load...
                    return;
                }
                else
                {
                    Kristall.Echo(DebugConsole.CCYellow + "Warning: Disposed Texture: " + FileName);
                }

            }

            Kristall.Echo(DebugConsole.CCCyan + "Loading " + FileName);

            if (!string.IsNullOrEmpty(FileName) && File.Exists(Kristall.FullContentPath + Kristall.DS + FileName))
            {
                if (Path.GetExtension(FileName) == ".png")
                {
                    SpriteTextures[FileName] = AnimatableSprite.loadTextureFromStream(Kristall.FullContentPath + Kristall.DS + FileName);
                }
                else
                {
                    SpriteTextures[FileName] = content.Load<Texture2D>(Path.ChangeExtension(FileName, null));
                }
            }
            else
            {
                Kristall.Echo(DebugConsole.CCRed + "Error Loading Texture: " + FileName);
            }
        }

        private static Texture2D loadTextureFromStream(string absolutePath)
        {
            FileStream fs = new FileStream(absolutePath, FileMode.Open);
            Texture2D texture = Texture2D.FromStream(Kristall.ScreenManager.GraphicsDevice, fs);
            fs.Close();

            return texture;
        }

        /// <summary>
        /// Load the sprite by absolute path (used by level editor when inserting new sprites)
        /// </summary>
        /// <param name="absolutePath"></param>
        /// <param name="content"></param>
        public static AnimatableSprite LoadForEditor(string absolutePath, Dictionary<string, Texture2D> SpriteTextures, ContentManager content)
        {
            if (File.Exists(absolutePath))
            {
                AnimatableSprite sprite = new AnimatableSprite();
                
                string relativeFilePath = absolutePath.Substring(Kristall.FullContentPath.Length + 1);
                sprite.FileName = relativeFilePath;

                if (!SpriteTextures.ContainsKey(sprite.FileName))
                {
                    //Load PNG-files using Texture2D.FromStream
                    string ext = Path.GetExtension(sprite.FileName).ToLower();
                    if (ext == ".png")
                    {
                        SpriteTextures[sprite.FileName] = loadTextureFromStream(absolutePath);
                    }
                    //XNB-files are loaded using the content manager
                    else if (ext == ".xnb")
                    {
                        string relativePathWithoutExtension = Path.ChangeExtension(relativeFilePath, null);
                        SpriteTextures[sprite.FileName] = content.Load<Texture2D>(relativePathWithoutExtension);
                    }
                    else
                    {
                        throw new Exception("Unsupported file format!");
                    }
                }

                if (SpriteTextures[sprite.FileName].IsDisposed)
                {
                    throw new Exception("Texture2D IsDisposed!?");
                }

                sprite.FrameWidth = SpriteTextures[sprite.FileName].Width;
                sprite.FrameHeight = SpriteTextures[sprite.FileName].Height;
                sprite.Origin = Vector2.Zero; //new Vector2(sprite.FrameWidth, sprite.FrameHeight) * 0.5f;

                return sprite;
            }
            else
            {
                throw new Exception("File not found!");
            }
        }

        void updateSource()
        {
            source = new Rectangle(frameIndex * FrameWidth, 1, FrameWidth, FrameHeight-1);
        }

        public void Update(float dt)
        {
            timer += dt;

            while (timer > FrameTime)
            {
                timer -= FrameTime;

                if (frameIndex == FrameCount - 1)
                {
                    if (OnAnimationEnd != null)
                    {
                        OnAnimationEnd(this, null);
                    }
                }
                else if (frameIndex == 0)
                {
                    if (OnAnimationStart != null)
                    {
                        OnAnimationStart(this, null);
                    }
                }

                if (IsLooping)
                {
                    frameIndex = (frameIndex + 1) % FrameCount;
                }
                else
                {
                    frameIndex = Math.Min(frameIndex + 1, FrameCount - 1);
                }

                updateSource();
            }
        }

        public void Draw(SpriteBatch batch, Vector2 position, float layerDepth)
        {
            if (SpriteTextures == null)
                return;

            if(SpriteTextures.ContainsKey(FileName))
            {
                SpriteEffects spriteEffect = SpriteEffects.None;

                if (Mirror)
                {
                    spriteEffect = SpriteEffects.FlipHorizontally;
                }

                //Rectangle rect = new Rectangle(position.X,position.Y,Scale)
                batch.Draw(SpriteTextures[FileName], position + Offset, source, Tint, Rotation, Origin, Scale, spriteEffect, layerDepth);
            }
            else if (!SpriteTextures.ContainsKey(FileName) || Kristall.EnableDebug)
            {
                batch.Draw(Kristall.Blank, new Rectangle((int)position.X, (int)position.Y, 100, 100), Color.Magenta);
            }
        }
    }

}
